#include "testbedGL.hpp"
#include <boost/thread.hpp>
#include "collada/collada.h"


TestbedGL::Window *window;
Quat quat;
Vector3 position;

TestbedGL::Scene scene;

Vector3 view;
using namespace  Vectormath::Aos;
Matrix3 mat;
using namespace TestbedGL;
Matrix4GL modelview,projection2;

Texture *tex,*bex,*depth;
RenderTarget *rt;
GLuint img;
Shader* shader;
ShaderVariable *mdlmtx,*prjmtx,*tex1,*tex2,*object,*eyePos;
ShaderAttribute *normals,*binormals,*tangents;

static void quit_program( int code )
{
    SDL_Quit( );
    exit( code );
}





static void process_events( void )
{

    SDL_Event event;
    while ( SDL_PollEvent( &event ) )
    {

        int x,y;
        float fx = x;
        float fy = y;
        static float xx = 0;
        static float yy = 0;
        //Matrix4 mat;
        TestbedGL::Matrix4GL mat;
        switch ( event.type )
        {
        case SDL_MOUSEMOTION:


            SDL_GetRelativeMouseState(&x, &y);
            fx = (float)x/(3.141*180);
            fy = (float)y/(3.141*180);
            xx +=fx;
            yy +=fy;
            mat = TestbedGL::Euler2Quat(xx,0,yy);
            view = Vector3(mat.getElem(0,2),mat.getElem(1,2),mat.getElem(2,2));
            view = normalize(view);
            modelview = Matrix4::lookAt(Point3(position),Point3(position+view),Vector3(0,1.0,0));
            break;

            // mouse down handler
        case SDL_MOUSEBUTTONDOWN:
            break;

            // mouse up handler
        case SDL_MOUSEBUTTONUP:
            break;

        case SDL_KEYDOWN:
            if (event.key.keysym.sym == SDLK_ESCAPE)
            {
                quit_program(0);
            }
            if (event.key.keysym.sym == SDLK_UP)
            {

                position+=view;
                modelview = Matrix4::lookAt(Point3(position),Point3(position+view),Vector3(0,1.0,0));

            }
            if (event.key.keysym.sym == SDLK_DOWN)
            {
                position-=view;
                modelview = Matrix4::lookAt(Point3(position),Point3(position+view),Vector3(0,1.0,0));

            }

            if (event.key.keysym.sym == SDLK_RIGHT)
            {

                position+=cross(view,Vector3(0,1,0));
                modelview = Matrix4::lookAt(Point3(position),Point3(position+view),Vector3(0,1.0,0));
            }

            if (event.key.keysym.sym == SDLK_LEFT)
            {

                position-=cross(view,Vector3(0,1,0));
                modelview = Matrix4::lookAt(Point3(position),Point3(position+view),Vector3(0,1.0,0));

            }

            break;
        case SDL_QUIT:
            /* Handle quit requests (like Ctrl-c). */
            quit_program( 0 );
            break;
        }

    }
}


void triangle()
{
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);

    // glLoadIdentity();

    //  rt->bind();

    //glClearColor(1.0,0.0,0.0,0.0);
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glDisable(GL_TEXTURE);
    //glActiveTexture(GL_TEXTURE0);
    //glBindTexture(GL_TEXTURE_2D,bex->handler);
    //glActiveTexture(GL_TEXTURE1);
    //glEnable(GL_TEXTURE);
    //glBindBuffer(GL_TEXTURE_2D,tex->handler);
    //shader->Bind();
    glEnable(GL_LIGHT0);
    glLoadIdentity();

    float pos[] = {100.0,80.0,70.0,1.0};

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glLoadMatrixf(modelview.getMatrix());

    glLightfv(GL_LIGHT0, GL_POSITION, pos);
    static float rot = 0;
    rot+=0.01;
    for (int i = 0; i < scene.nodes.size(); i++)
    {
        for (int j = 0; j < scene.nodes[i].geometry_ids.size(); j++)
        {
            glPushMatrix();
            glMultMatrixf(scene.nodes[i].base_matrix.getMatrix());
            scene.geometries[scene.nodes[i].geometry_ids[j]].bind();
            glDrawElements( GL_TRIANGLES, scene.geometries[scene.nodes[i].geometry_ids[j]].faces.size, GL_UNSIGNED_INT, scene.geometries[scene.nodes[i].geometry_ids[j]].faces.array.get());

            glPopMatrix();
        }
    }

    Matrix4GL identity = Matrix4::identity();


    // glEnable(GL_TEXTURE_2D);

    // glBindTexture(GL_TEXTURE_2D,tex->handler);
    // glGenerateMipmapEXT(GL_TEXTURE_2D);
    // glBegin(GL_QUADS);
    // glVertex2f(0,0);
    // glNormal3f(0,0,1.0);
    // glTexCoord2f(0,0);

    // glVertex2f(1,0);
    // glNormal3f(0,0,1.0);
    // glTexCoord2f(0,1);

    // glVertex2f(1,1);
    // glNormal3f(0,0,1.0);
    // glTexCoord2f(1,1);

    // glVertex2f(0,1);
    // glNormal3f(0,0,1.0);
    // glTexCoord2f(1,0);
    // glEnd();
    glBindTexture(GL_TEXTURE_2D,0);
    //modelview= tmp;

}

shared_array<unsigned int> vector_to_uarray(std::vector<unsigned int>&vec)
{
    shared_array<unsigned int> ret (new unsigned int[vec.size()]);
    for (unsigned int i = 0; i < vec.size(); i++)
        ret[i] = vec[i];
    return ret;
}

shared_array<float> vector_to_array(std::vector<float>&vec)
{
    shared_array<float> ret (new float[vec.size()]);
    for (unsigned int i = 0; i < vec.size(); i++)
        ret[i] = vec[i];
    return ret;
}

char *data;
#ifdef WIN32
#include <windows.h>
int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow)
#else
int main(int argc, const char **params)
#endif
{
    window = new TestbedGL::Window(1024,768,24);

    window->SetRenderViewport(1024,768,55.0,1.0,10000.0);

    window->drawFrameFunc = triangle;

    Collada::Parser ps;


    try
    {
        ps.Parse("i5.dae");
    }
    catch (const char *str)
    {
        std::cout << str <<std::endl;
    }

    std::vector<Collada::DictionaryEntry*>entries;

    ps.dict.GetElementsByType("node",entries);

    for (std::vector<Collada::DictionaryEntry*>::iterator it = entries.begin(); it != entries.end(); it++)
    {
        Collada::Node* node = ((Collada::Node*)(*it));
        if (((Collada::Node*)(*it))->instance_geometry.size())
        {

            Collada::Geometry* geo = (Collada::Geometry*)ps.dict.GetEntry(((Collada::Node*)(*it))->instance_geometry);
            shared_ptr<Geometry> geom(new Geometry());
            geom->name =geo->id;

            scene.nodes.push_back(SceneNode());
            scene.nodes.rbegin()->base_matrix = Matrix4GL::identity();
            std::cout << node->transforms.size() ;
            for (std::vector<Collada::Transform>::iterator tr_it = node->transforms.begin(); tr_it != node->transforms.end(); tr_it++)
            {
                std::cout << tr_it->name << std::endl;
                if (tr_it->name == "translate")
                {
                    scene.nodes.rbegin()->base_matrix *= scene.nodes.rbegin()->base_matrix.translation(Vector3(tr_it->values[0],tr_it->values[1],tr_it->values[2]));

                }
                else if (tr_it->name == "scale")
                {
                    scene.nodes.rbegin()->base_matrix *= scene.nodes.rbegin()->base_matrix.scale(Vector3(tr_it->values[0],tr_it->values[1],tr_it->values[2]));

                }
                else if (tr_it->name == "rotate")
                {
                    scene.nodes.rbegin()->base_matrix *= scene.nodes.rbegin()->base_matrix.rotation(tr_it->values[3]/180.0*3.14159,Vector3(tr_it->values[0],tr_it->values[1],tr_it->values[2]));

                }

            }

            for (std::vector<std::string>::iterator tri_it = geo->triangles.begin(); tri_it != geo->triangles.end(); tri_it++)
            {
                Collada::Triangles* tris = (Collada::Triangles*)ps.dict.GetEntry(*tri_it);
                FaceArray faces(tris->indecies->array.size(),vector_to_uarray(tris->indecies->array));
                geom->faces = faces;
                for (std::vector<std::string>::iterator src_it = tris->inputs.begin(); src_it != tris->inputs.end(); src_it++)
                {
                    std::string name = *src_it;
                    if (geo->vertices.count(name))
                    {
                        for (std::vector<std::string>::iterator v_it = geo->vertices[name].begin(); v_it != geo->vertices[name].end(); v_it++)
                        {
                            Collada::Source *src = (Collada::Source*)ps.dict.GetEntry(*v_it);
                            FloatArray pos(src->stride,
                                           src->array->array.size(),src->stride,
                                           vector_to_array(src->array->array),
                                           src->semantic);

                            if (src->semantic.compare("POSITION")==0)
                                geom->link.set_vertex_array(pos);
                        }
                    }
                    else
                    {
                        Collada::Source *src = (Collada::Source*)ps.dict.GetEntry(name);
                        FloatArray pos(src->stride,src->array->array.size(),src->stride,
                                       vector_to_array(src->array->array),src->semantic);
                        if (src->semantic.compare("NORMAL")==0)
                            geom->link.set_normal_array(pos);
                        else if (src->semantic.compare("TEXCOORD")==0)
                            geom->link.add_tex_coord_array(pos);
                    }
                }
                scene.geometries.push_back(*geom);

                std::cout << geom->name;
                scene.nodes.rbegin()->geometry_ids.push_back(scene.geometries.size()-1);
            }
            //FloatArray pos(scn.geometries[i].inputs[0].width,scn.geometries[i].inputs[0].size,scn.geometries[i].inputs[0].width,scn.geometries[i].inputs[0].array,"positions")
        }
    }
    //FloatArray pos(scn.geometries[i].inputs[0].width,scn.geometries[i].inputs[0].size,scn.geometries[i].inputs[0].width,scn.geometries[i].inputs[0].array,"positions");
    //FloatArray norms(scn.geometries[i].inputs[1].width,scn.geometries[i].inputs[1].size,scn.geometries[i].inputs[1].width,scn.geometries[i].inputs[1].array,"normalss");

    //~ for(int i = 0; i < scn.geometries.size(); i++)
    //~ {
    //~ if(scn.geometries[i].count==0) continue;
    //~ shared_ptr<Geometry> geom(new Geometry());
    //~ geom->name = scn.geometries[i].name;
    //~ FloatArray pos(scn.geometries[i].inputs[0].width,scn.geometries[i].inputs[0].size,scn.geometries[i].inputs[0].width,scn.geometries[i].inputs[0].array,"positions");
    //~ FloatArray norms(scn.geometries[i].inputs[1].width,scn.geometries[i].inputs[1].size,scn.geometries[i].inputs[1].width,scn.geometries[i].inputs[1].array,"normalss");
    //~ //printf("%s\n",scn.geometries[i].inputs[1].name.c_str());
    //~ // FloatArray tex_coord(scn.geometries[i].inputs[2].width,scn.geometries[i].inputs[2].size,scn.geometries[i].inputs[2].width,scn.geometries[i].inputs[2].array,"tex_coords");

    //~ geom->link.set_vertex_array(pos);
    //~ //geom->link.add_tex_coord_array(tex_coord);
    //~ geom->link.set_normal_array(norms);
    //~ //printf("%d\n",scn.geometries[i].count);
    //~ shared_array<unsigned int> indc(new unsigned int[scn.geometries[i].count]);
    //~ for(unsigned int sz = 0; sz < scn.geometries[i].count; sz++)
    //~ indc[sz] = scn.geometries[i].indecies[sz];

    //~ FaceArray faces(scn.geometries[i].count,indc);
    //~ geom->faces = faces;
    //~ scene.geometries.push_back(*geom);
    //~ }

    //~ for(int i = 0; i < scn.nodes.size(); i++)
    //~ {
    //~ scene.nodes.push_back(SceneNode());
    //~ scene.nodes.rbegin()->base_matrix = scn.nodes[i].matrix;
    //~ for(int j = 0; j < scn.nodes[i].geometry_urls.size(); j++)
    //~ {
    //~ //int id = scene.Geom(scn.nodes[i].geometry_urls[j].name);
    //~ // if(scn.geometries[id].count>0)
    //~ scene.nodes.rbegin()->geometry_ids.push_back(scene.Geom(scn.nodes[i].geometry_urls[j].name));
    //~ }
    //~ //scene.nodes.rbegin()->
    //~ }
    //printf("GEOMETRY LOADED\n");

//    node->base_matrix = Matrix4GL::identity();
    modelview = Matrix4::translation(Vector3(0,0,1.0));
    projection2 = Matrix4::perspective(50.0/180*3.14159,1024.0/768.0,0.1,1000);
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(projection2.getMatrix());
    glMatrixMode(GL_MODELVIEW);

    // tex = new Texture();
    // tex->loadimage("bump2.tga");



    // bex = new Texture();
    // bex->loadimage("bump1.tga");
    //Loading shader
    // shader = new Shader("shader.vs","shader.fs");
    // prjmtx = new ShaderVariable(*shader,"projection");
    // mdlmtx = new ShaderVariable(*shader,"modelview");
    // object = new ShaderVariable(*shader,"object");
//    eyePos = new ShaderVariable(*shader,"Eye");
//
//    tex1 = new ShaderVariable(*shader,"texture");
////    tex2 = new ShaderVariable(*shader,"texture2");
//    binormals = new ShaderAttribute(*shader,"binormal");
//    tangents = new ShaderAttribute(*shader,"tangent");


//    node->geometries[0]->link.arrays[3].id = binormals->location;
//    node->geometries[0]->link.arrays[4].id = tangents->location;


//    std::cout << node->children[0].geometries[0].icount;
    while (1)
    {
        window->DrawFrame();
        process_events( );

    }
    return 0;
}
